package com.jidolstar.astro
{
	/**
	 * 3D 표현 벡터 
	 * @author Yongho,Ji (jidolstar@gmail.com)
	 * @since 2009.04.07
	 */ 
	public class ALVector3D
	{
		public static const COORDINATE_TYPE_RECTANGLE:String = "Rectangle";
		public static const COORDINATE_TYPE_SPHERICAL:String = "Spherical";
		private static const TPI:Number = ALConst.TPI;
		
		private var _x:Number;
		private var _y:Number;
		private var _z:Number;
		private var _length:Number;
		private var _length2:Number;
		private var _longitude:Number;
		private var _latitude:Number;
		private var _longitudeDegree:Number;
		private var _longitudeHour:Number;
		private var _latitudeDegree:Number;

		private var isSetLength:Boolean = false;
		private var isSetLength2:Boolean = false;
		private var isSetSphe:Boolean = false;
		private var isSetLongitudeDegree:Boolean = false;
		private var isSetLongitudeHour:Boolean = false;
		private var isSetLatitudeDegree:Boolean = false;
		
		/**
		 * 생성자 
		 * @param a 직각좌표의 x 또는 구면좌표의 longitude(radian)
		 * @param b 직각좌표의 y 또는 구면좌표의 latitude(radian)
		 * @param c 직각좌표의 z 또는 구면좌표의 length
		 * @param coordinateType 
		 * 		ALVector3D.COORDINATE_TYPE_RECTANGLE 또는 ALVector3D.COORDINATE_TYPE_SPHERICAL
		 * 		a,b,c값이 직각좌표계 또는 구면좌표계의 좌표값인가 구분하는 인자임 
		 */ 
		public function ALVector3D( a:Number = 0, b:Number = 0, c:Number = 0, coordinateType:String = "Rectangle")
		{
			switch( coordinateType )
			{
				case COORDINATE_TYPE_RECTANGLE:
					setRect( a, b, c );
					break;
				case COORDINATE_TYPE_SPHERICAL:
					setSphe( a, b, c );	
					break;
				default:
					throw new ArgumentError( "coordinate type is wrong value" );
					break;
			}
			
		}
		
		public function get x():Number
		{
			return _x;
		}
		
		public function get y():Number
		{
			return _y;
		}
		
		public function get z():Number
		{
			return _z;
		}
		
		/**
		 * |Vector| 백터 크기 반환 
		 */ 
		public function get length():Number
		{
			if( !isSetLength ) 
			{
				isSetLength = true;
				_length = Math.sqrt( _x*_x + _y*_y + _z*_z );
			}
			return _length;
		}	
		
		/**
		 * |Vector| 백터 크기^2 
		 */ 
		public function get length2():Number
		{
			if( !isSetLength2 ) 
			{
				isSetLength2 = true;
				_length2 = _x*_x + _y*_y + _z*_z;
			}
			return _length2;
		}
		
		/**
		 * 백터의 Longitude값. radian 단위값이다.
		 */ 
		public function get longitude():Number
		{
			if( !isSetSphe ) 
			{
				isSetSphe = true;
				_longitude = Math.atan2( _y, _x );
				_latitude = Math.asin( _z / length );			
			} 
			return _longitude;
		}
		
		/**
		 * 백터의 Latitude값 radian 단위값이다.
		 */ 
		public function get latitude():Number
		{
			if( !isSetSphe ) 
			{
				isSetSphe = true;
				_longitude = Math.atan2( _y, _x );	
				_latitude = Math.asin( z / length );			
			} 
			return _latitude;			
		}
		
		public function get longitudeDegree():Number
		{
			if( !isSetLongitudeDegree )
			{
				isSetLongitudeDegree = true;
				_longitudeDegree = ALMath.mapTo0To360Range( longitude * ALConst.R2D );
			}
			return _longitudeDegree;
		}
		
		public function get longitudeHour():Number
		{
			if( !isSetLongitudeHour )
			{
				isSetLongitudeHour = true;
				_longitudeHour = ALMath.mapTo0To24Range( longitude * ALConst.R2H );
			}
			return _longitudeHour;
		}
		
		public function get latitudeDegree():Number
		{
			if( !isSetLatitudeDegree )
			{
				isSetLatitudeDegree = true;
				_latitudeDegree = ALMath.mapToMinus90To90Range( latitude * ALConst.R2D );
			}
			return _latitudeDegree;
		}

		/**
		 * Spherical Coodinate -> Rectagular Coordinate
		 */ 
		public function setSphe(longitudeRadian:Number, latitudeRadian:Number, length:Number=1):void
		{
			var cos_lon_value:Number = Math.cos(longitudeRadian);
			var sin_lon_value:Number = Math.sin(longitudeRadian);
			var cos_lat_value:Number = Math.cos(latitudeRadian);
			var sin_lat_value:Number = Math.sin(latitudeRadian);
			_x = length * cos_lon_value * cos_lat_value;
			_y = length * sin_lon_value * cos_lat_value;
			_z = length * sin_lat_value;
			_longitude = longitudeRadian;
			_latitude = latitudeRadian;
			_length = length;
			isSetLength = true;
			isSetLength2 = false;
			isSetSphe = true;
			isSetLongitudeDegree = false;
			isSetLongitudeHour = false;
			isSetLatitudeDegree = false;
		}
		
		/**
		 * Rectagular Coordinate -> Spherical Coodinate
		 */ 
		public function setRect(x:Number, y:Number, z:Number):void
		{
			this._x = x;
			this._y = y;
			this._z = z;	
			isSetLength = false;
			isSetLength2 = false;
			isSetSphe = false;	
			isSetLongitudeDegree = false;
			isSetLongitudeHour = false;
			isSetLatitudeDegree = false;
		}
		
		/**
		 * Vector = Vector * Number
		 */ 
		public function multiplyNumber( value:Number ):void
		{
			_x *= value;
			_y *= value;
			_z *= value;
		}							
		
		/**
		 * x,y,z값 출력 
		 */ 
		public function toString():String
		{
			return String(x+","+y+","+z);
		}	
		
		/**
		 * Vector3 = Vector1 + Vecter2 
		 * @example
		 * <pre>
		 * var v1:ALVector3D = new ALVector3D( 10, 10, 10 );
		 * var v2:ALVector3D = new ALVector3D( 10, 10, 10 );
		 * var v3:ALVector3D = ALVector3D.add( v1, v2 );
		 * trace( v3.toString() ); //20,20,20
		 * </pre>
		 */ 
		static public function add(v1:ALVector3D, v2:ALVector3D):ALVector3D
		{
			return new ALVector3D( v1._x + v2._x, v1._y + v2._y, v1._z + v2._z );
		}	
		
		/**
		 * Vector3 = Vector1 - Vecter2 
		 * <pre>
		 * var v1:ALVector3D = new ALVector3D( 10, 10, 10 );
		 * var v2:ALVector3D = new ALVector3D( 10, 10, 10 );
		 * var v3:ALVector3D = ALVector3D.sub( v1, v2 );
		 * trace( v3.toString() ); //0,0,0
		 * </pre>
		 */ 
		static public function sub(v1:ALVector3D, v2:ALVector3D):ALVector3D
		{
			return new ALVector3D( v1._x - v2._x, v1._y - v2._y, v1._z - v2._z);
		}
		
		/**
		 * dot product
		 */ 
		static public function dotProduct(v1:ALVector3D, v2:ALVector3D):Number
		{
			return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z;			
		}
		
		/**
		 * cross product 
		 */ 
		static public function crossProduct(v1:ALVector3D, v2:ALVector3D):ALVector3D
		{
			var a1:Number = v1.x;
			var a2:Number = v1.y;
			var a3:Number = v1.z;
			var b1:Number = v2.x;
			var b2:Number = v2.y;
			var b3:Number = v2.z;	
			return new ALVector3D( a2*b3 - a3*b2, a3*b1 - a1*b3, a1*b2 - a2*b1 );		
		}
		
		/**
		 * 숫자 곱해서 새로운 Vector 생성  
		 */ 
		static public function multiplyNumber( vector:ALVector3D, num:Number ):ALVector3D
		{
			return new ALVector3D( vector._x * num, vector._y * num, vector._z * num ); 
		}							
	}
}